package client

import (
	"fmt"
	"strconv"
	"time"

	"github.com/goburrow/modbus"
)

// ModbusTCP tcp客户端
type ModbusTCP struct {
	tcpModbusClient  modbus.Client
	tcpClientHandler *modbus.TCPClientHandler
}

// Open 打开连接
// 需要配置:
//
//	modbus.tcp.address 	ip地址
//	modbus.tcp.port    	端口
//	modbus.time_out  	超时时间
//	modbus.slave_id  	编号
func (modbusTcp *ModbusTCP) Open(config map[string]interface{}) error {

	h, ok := config[host]
	p, ok2 := config[port]
	if !ok || !ok2 || nil == h || p == nil {
		return fmt.Errorf("modbus的配置错误,请检查地址[%v]和端口[%v]配置", host, port)
	}
	url := fmt.Sprintf("%v:%v", h, p)
	v, ok := config[timeOut]
	ito := 500
	var e error = nil
	if ok && v != nil {
		iTimeOut := fmt.Sprintf("%d", v)
		ito, e = strconv.Atoi(iTimeOut)
	}
	if e != nil {
		ito = 500
	}

	v, ok = config[slaveId]
	if !ok {
		return fmt.Errorf("modbus的SlaveID配置错误,请检查地址[%v]的配置", slaveId)
	}
	sid := 254
	if ok && v != nil {
		s := fmt.Sprintf("%d", v)
		sid, _ = strconv.Atoi(s)
	}
	handler := modbus.NewTCPClientHandler(url)
	handler.SlaveId = byte(sid)
	handler.Timeout = time.Duration(ito) * time.Millisecond
	modbusTcp.tcpClientHandler = handler
	modbusTcp.tcpModbusClient = modbus.NewClient(handler)
	err := handler.Connect()
	if err != nil {
		return fmt.Errorf("连接到%v失败,错误:%v", url, err)
	} else {
		return nil
	}
}

// Close 关闭连接
func (modbusTcp *ModbusTCP) Close() {
	if modbusTcp.tcpClientHandler != nil {
		_ = modbusTcp.tcpClientHandler.Close()
	}
	modbusTcp.tcpClientHandler = nil
}

// SetSlaveID set slave id
func (modbusTcp *ModbusTCP) SetSlaveID(slaveID byte) {
	modbusTcp.tcpClientHandler.SlaveId = slaveID
}

// ReadCoils 读1到2000的线圈状态
func (modbusTcp *ModbusTCP) ReadCoils(address, quantity uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadCoils(address, quantity)
}

// ReadDiscreteInputs 读取1到2000的离散输入值
func (modbusTcp *ModbusTCP) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadDiscreteInputs(address, quantity)
}

// WriteSingleCoil 向线圈写单个开关量输入并返回
func (modbusTcp *ModbusTCP) WriteSingleCoil(address, value uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.WriteSingleCoil(address, value)
}

// WriteMultipleCoils forces each coil in a sequence of coils to either
// ON or OFF in a remote device and returns quantity of outputs.
func (modbusTcp *ModbusTCP) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.WriteMultipleCoils(address, quantity, value)
}

// 16-bit access

// ReadInputRegisters reads from 1 to 125 contiguous input registers in
// a remote device and returns input registers.
func (modbusTcp *ModbusTCP) ReadInputRegisters(address, quantity uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadInputRegisters(address, quantity)
}

// ReadHoldingRegisters reads the contents of a contiguous block of
// holding registers in a remote device and returns register value.
func (modbusTcp *ModbusTCP) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadHoldingRegisters(address, quantity)
}

// WriteSingleRegister writes a single holding register in a remote
// device and returns register value.
func (modbusTcp *ModbusTCP) WriteSingleRegister(address, value uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.WriteSingleRegister(address, value)
}

// WriteMultipleRegisters writes a block of contiguous registers
// (1 to 123 registers) in a remote device and returns quantity of
// registers.
func (modbusTcp *ModbusTCP) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.WriteMultipleRegisters(address, quantity, value)
}

// ReadWriteMultipleRegisters performs a combination of one read
// operation and one write operation. It returns read registers value.
func (modbusTcp *ModbusTCP) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity, value)
}

// MaskWriteRegister modify the contents of a specified holding
// register using a combination of an AND mask, an OR mask, and the
// register'limiter current contents. The function returns
// AND-mask and OR-mask.
func (modbusTcp *ModbusTCP) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.MaskWriteRegister(address, andMask, orMask)
}

// ReadFIFOQueue reads the contents of a First-In-First-Out (FIFO) queue
// of register in a remote device and returns FIFO value register.
func (modbusTcp *ModbusTCP) ReadFIFOQueue(address uint16) (results []byte, err error) {
	return modbusTcp.tcpModbusClient.ReadFIFOQueue(address)
}
